/*
 ==============================================================================
 
 KSVoice.cpp
 Created: 25 Nov 2014 4:34:07pm
 Author:  Alistair Barker
 
 ==============================================================================
 */

#include "KSVoice.h"
#include "PluginProcessor.h"

KSVoice::KSVoice(KarplusStrongAudioProcessor& p): processor (p) 
{
    
    state = off;
    
    generator = new Generator;
    generator->setFrequency(1000);
    generator->setOutputType(generator->kWhiteNoise);
    generator->setSampleRate(getSampleRate());
    
    
    noiseEnv = new Envelope(processor.paramAttack, processor.paramAttack, 0.0, processor.paramAttack, getSampleRate());
    
    processor.paramNumOfStrings.addListener(this);
    changeNumDelays(processor.getParameter(kParameter_StringNo));
    
    DBG("Construcing a voice");
}

KSVoice::~KSVoice()
{
    processor.paramNumOfStrings.removeListener(this);
    delete generator;
    delete noiseEnv;
}

void KSVoice::changeNumDelays(int newNumStrings)
{
    IIRCoefficients fbCoeffs = IIRCoefficients::makeLowPass(getSampleRate(), processor.getParameter(kParameter_hfDamping));

    while (newNumStrings > delays.size()) {
        delays.add(new DelayLine() );
        feedbackFilters.add (new IIRFilter());
        feedbackFilters.getLast()->setCoefficients(fbCoeffs);
    }
    
    while (newNumStrings < delays.size()) {
        delays.remove(delays.size() - 1);
        feedbackFilters.remove(feedbackFilters.size() - 1);
    }
    
}

void KSVoice::valueChanged (Value& value)
{
    changeNumDelays(static_cast<int>(processor.paramNumOfStrings.getValue()));
    
}

bool KSVoice::canPlaySound (SynthesiserSound* sound)
{
    return dynamic_cast <KSSound*> (sound) != 0;
}

void KSVoice::startNote (const int midiNoteNumber,
                         const float velocity,
                         SynthesiserSound* sound,
                         const int currentPitchWheelPosition)
{
    
    if (state == off ) {
        noiseEnv->setEnvelope(processor.paramAttack, 0.01, 0.0, 0.01, getSampleRate());
        noiseEnv->noteOn(velocity);
    }
    
    currentNote = midiNoteNumber;
    
    double delTime = getSampleRate()/ MidiMessage::getMidiNoteInHertz(midiNoteNumber);
    
    float detune = processor.getParameter(kParameter_Detune);
    
    setResonance(currentNote);
    
    state = impulse;
    double glissando = processor.getParameter(kParameter_glissando);
    
    DBG("Note On. Velocity: " + String(velocity)  + " Note number: " + String(midiNoteNumber));
    
    for (int i = 0; i < delays.size() ; i++) {
        double indivDetune;
        indivDetune = 1 + detune*(i-((delays.size()-1)/2));
        delays[i]->setDelayWithGliss(indivDetune*delTime, glissando, processor.getSampleRate());
        feedbackFilters[i]->setCoefficients(IIRCoefficients::makeLowPass(getSampleRate(), processor.getParameter(kParameter_hfDamping)));
    }
}

void KSVoice::stopNote (float velocity, bool allowTailOff)
{
    noiseEnv->noteOff();
    
    if (!allowTailOff) {
        state = off;
        clearCurrentNote();
    }
}

void KSVoice::setResonance(int note)
{
    resonance = pow(processor.getParameter(kParameter_Resonance), pow(2, (60-note)/12.0));
}

double KSVoice::getExcitationSample()
{
    float gain = 0.0125;
    
    if (noiseEnv->getState() == Envelope::kIdle && state ==impulse) {
        state = tail;
    }
    
    //        multiply sample by envelope and apply const gain
    return generator->getNextSample() * noiseEnv->getLinearGainAndTick() * gain;

}

bool KSVoice::checkRMS(double input)
{
    bool value;
    if (RMSChecker.calculateRMS(input) > 0.0001)
    {
        value = true;
    }
    else
    {
        value = false;
    }
    
    return value;
    
}

void KSVoice::renderNextBlock (AudioSampleBuffer& outputBuffer,
                               int startSample,
                               int numSamples)
{
    
    
    while (--numSamples >= 0) {
        
        if (state == off) {
            break;
        }
        
        double outputMix = 0.0;
        
        double excitationOutput = getExcitationSample();
        
        setResonance(currentNote);
        
        double input;
        
        for (int i = 0; i < delays.size(); i++) {
            input = excitationOutput + feedbackFilters[i]->processSingleSampleRaw(delays[i]->read()) * resonance;
            delays[i]->write(input);
            outputMix += input;
        }
        
        
        
        for (int i = 0; i < outputBuffer.getNumChannels(); i++) {
            outputBuffer.addSample (i, startSample, outputMix);
        }
        
//        RMSValue = calculateRMS(outputMix);
        
        if (state == tail) {         //Check RMS and turn note off if needed.
            if (checkRMS(outputMix) == false) {
                clearCurrentNote();
                DBG("Note cleared");
                state = off;
            }
        }
        
        startSample++;
    }
}